"""
$Id: mutate.py 2 2007-03-04 19:08:44Z sidnei.da.silva $
"""

from evolve.choice import randomize, choice
from evolve.genome import Genome

class Mutate(object):
    """Mutate policy for genome
    """

    def __call__(self, genome):
        raise NotImplementedError, self.__class__

class SimpleMutate(Mutate):

    def __init__(self, chance=0.01, choice=randomize):
        self.chance = chance
        self.choice = choice

    def __call__(self, genome):
        """Only mutate if choice is smaller than chance
        """
        if self.choice() > self.chance:
            return genome


class Rotate(SimpleMutate):
    """Rotate mutation

    Will rotate the genes inside the genome by one
    """

    def __call__(self, genome):
        result = super(Rotate, self).__call__(genome)
        if result is not None:
            return result
        return Genome(genome[-1], *genome[:-1])

class RandomRotate(SimpleMutate):
    """Random Rotate mutation

    Will rotate the genes inside the genome by a random amount (max 25%)
    """

    def __call__(self, genome):
        result = super(RandomRotate, self).__call__(genome)
        if result is not None:
            return result
        step = int(self.choice() * len(genome) / 4)
        rotated = genome[step:] + genome[:step]
        return Genome(*rotated)

class RandomSwap(SimpleMutate):
    """Random swap mutation

    Will swap two genes inside the genome
    """

    def __init__(self, chance=0.01, choice=randomize, rounds=1):
        super(RandomSwap, self).__init__(chance, choice)
        self.rounds = rounds

    def __call__(self, genome):
        result = super(RandomSwap, self).__call__(genome)
        if result is not None:
            return result
        genome = list(genome)
        for i in range(self.rounds):
            g1 = choice(genome)
            g2 = choice(genome)
            while g2 == g1:
                g2 = choice(genome)
            i1 = genome.index(g1)
            i2 = genome.index(g2)
            genome[i1] = g2
            genome[i2] = g1
        return Genome(*genome)

class RandomSwapRotate(SimpleMutate):

    def __init__(self, chance=0.01, choice=randomize):
        super(RandomSwapRotate, self).__init__(chance, choice)
        self.swap = RandomSwap(chance=1, choice=choice)
        self.rotate = RandomRotate(chance=1, choice=choice)
        self.fixed = FixedSwap(chance=1, steps=((0, -1),))

        def generator():
            while True:
                yield self.swap
                yield self.rotate
                yield self.fixed

        self.generator = generator()


    def __call__(self, genome):
        result = super(RandomSwapRotate, self).__call__(genome)
        if result is not None:
            return result
        mutator = self.generator.next()
        return mutator(genome)

class StepsMutate(SimpleMutate):

    def __init__(self, chance=0.01, choice=randomize, steps=((-2, -1),)):
        super(StepsMutate, self).__init__(chance, choice)
        self.steps = steps

class FixedSwap(StepsMutate):
    """Fixed swap mutation

    Will swap two genes inside the genome
    """

    def __call__(self, genome):
        result = super(FixedSwap, self).__call__(genome)
        if result is not None:
            return result
        genome = list(genome)
        for x, y in self.steps:
            g1 = genome[x]
            g2 = genome[y]
            genome[x] = g2
            genome[y] = g1
        return Genome(*genome)
